#include "process_helper.h"

#include <fcntl.h>  // For open()
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <signal.h>
#include <sys/stat.h>  // For chmod()
#include <sys/wait.h>
#include <unistd.h>

#include <fstream>  // For file operations
#include <iostream>
using namespace testing;

// Test fixture for process_helper functions
class ProcessHelperTest : public ::testing::Test {
 protected:
  pid_t dummy_process_pid{-1};
  std::string dummy_script_path =
      "./dummy_process";  // Path to dummy_process shell script

  void SetUp() override {
    // Create dummy_process shell script
    CreateDummyProcessScript(dummy_script_path);

    // Start dummy_process before each test
    dummy_process_pid = StartDummyProcess();
    ASSERT_NE(dummy_process_pid, -1);  // Check if StartDummyProcess() succeeded
    usleep(1000 * 100);  // sleep 100ms, wait for process status ready
  }

  void TearDown() override {
    // Terminate dummy_process after each test
    TerminateDummyProcess(dummy_process_pid);

    // Clean up dummy_process shell script
    std::remove(dummy_script_path.c_str());
  }

  // Helper function to create dummy_process shell script with signal handling
  void CreateDummyProcessScript(const std::string& script_path) {
    std::ofstream script_file(script_path);
    if (script_file.is_open()) {
      script_file << "#!/bin/bash\n";
      script_file << "\n";
      script_file << "# Function to handle signals\n";
      script_file << "function handle_signal() {\n";
      script_file << "    echo \"[dummy_process] Received signal: $1\"\n";
      script_file << "    exit 0\n";  // Exit gracefully on signal
      script_file << "}\n";
      script_file << "\n";
      script_file << "# Trap signals\n";
      script_file
          << "trap 'handle_signal SIGKILL' SIGKILL\n";  // Handle SIGKILL signal

      script_file << "\n";
      script_file << "# Main loop\n";
      script_file << "while true; do\n";
      script_file << "    echo \"dummy_process is running...\"\n";
      script_file << "    sleep 1\n";
      script_file << "done\n";
      script_file << "\n";
      script_file << "echo \"dummy_process is exit!\"\n";
      script_file << "exec -a dummy_process ./dummy_process\n";

      script_file.close();

      // Add executable permission to the script
      if (chmod(script_path.c_str(),
                S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
        std::cerr << "Failed to set execute permission on " << script_path
                  << std::endl;
        exit(EXIT_FAILURE);
      }
    } else {
      std::cerr << "Failed to create " << script_path << std::endl;
      exit(EXIT_FAILURE);
    }
  }

  // Helper function to start dummy_process
  pid_t StartDummyProcess() {
    pid_t pid = fork();
    if (pid == -1) {
      std::cerr << "Failed to fork() process." << std::endl;
      return -1;
    } else if (pid == 0) {
      // Child process: execute dummy_process script
      execlp(dummy_script_path.c_str(), dummy_script_path.c_str(), nullptr);

      // execlp should not return if successful
      std::cerr << "Failed to exec " << dummy_script_path << std::endl;
      exit(EXIT_FAILURE);
    } else {
      // Parent process: return child process PID
      return pid;
    }
  }

  // Helper function to terminate dummy_process
  void TerminateDummyProcess(pid_t pid) {
    // Send SIGKILL to dummy_process
    kill(pid, SIGKILL);
    // Wait for child process to end
    int status;
    waitpid(pid, &status, 0);
  }

  // Helper function to check if a process is running by name
  bool IsProcessRunning(const std::string& process_name) {
    pid_t pid;
    return FindPidByProcessName(process_name, pid);
  }
};

// Test for FreezeProcessByName function
TEST_F(ProcessHelperTest, FreezeProcessByNameTest) {
  int result = FreezeProcessByName("dummy_process");
  EXPECT_EQ(result, 0);
  ASSERT_TRUE(IsProcessRunning("dummy_process"));
  ASSERT_TRUE(IsProcessStopped(dummy_process_pid));
}

// Test for TryToResumeProcessByName function
TEST_F(ProcessHelperTest, TryToResumeProcessByNameTest) {
  // Invoke the function under test
  int result = TryToResumeProcessByName("dummy_process", 3);

  EXPECT_EQ(result, 0);
  ASSERT_TRUE(IsProcessRunning("dummy_process"));
  ASSERT_TRUE(!IsProcessStopped(dummy_process_pid));
}

// Test for FreezeProcessByName function
TEST_F(ProcessHelperTest, FreezeProcessByPidTest) {
  pid_t pid;
  bool find = FindPidByProcessName("dummy_process", pid);
  EXPECT_TRUE(find);
  bool result = FreezeProcessByPid(pid);
  EXPECT_TRUE(result);
  ASSERT_TRUE(IsProcessRunning("dummy_process"));
  ASSERT_TRUE(IsProcessStopped(dummy_process_pid));
}

// Test for TryToResumeProcessByName function
TEST_F(ProcessHelperTest, TryToResumeProcessByPidTest) {
  pid_t pid;
  bool find = FindPidByProcessName("dummy_process", pid);
  EXPECT_TRUE(find);

  bool result = TryToResumeProcessByPid(pid, 3);
  EXPECT_TRUE(result);
  ASSERT_TRUE(IsProcessRunning("dummy_process"));
  ASSERT_TRUE(!IsProcessStopped(dummy_process_pid));
}

// Test for TerminateProcess function
TEST_F(ProcessHelperTest, TerminateProcessTest) {
  int result = TerminateProcess("dummy_process", 3);

  EXPECT_EQ(result, 0);
  ASSERT_FALSE(IsProcessRunning("dummy_process"));
}

// Example test case for IsProcessNotStopped function (not directly called, but
// used internally)
TEST(IsProcessStoppedTest, IsStoppedTest) {
  // Assuming a process is not stopped with a valid PID
  pid_t pid = 123456;
  EXPECT_TRUE(IsProcessStopped(pid));
}

int main(int argc, char** argv) {
  testing::InitGoogleMock(&argc, argv);
  return RUN_ALL_TESTS();
}
